/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
#include <memory>
#include"WorkPingApi.h"
#include"WorkPriApi.h"
#ifndef NOUSE_PUBLIC_LOG
#include"PublicLog.h"
#endif
#if USE_PUBLIC_PING
#define DEFAULT_MAX_TIMES 20
typedef struct _PINGTTLINFOTMP{
char fromip[DEFAULT_MAX_TIMES][40];
int  ping[DEFAULT_MAX_TIMES];
}PINGTTLINFOTMP;
typedef std::map<std::string,PINGTTLINFOTMP>MAPPINGTTLINFOTMP;
MAPTTLINFO GetLossStatusByTmp(MAPPINGTTLINFOTMP tmpstatus);
MAPTTLINFO static _GetTTLInfoByIPList(SETIPLIST IPList,PINGFLAG flag);

MAPTTLINFO GetTTLInfoByIPList(SETIPLIST IPList,PINGFLAG flag){
	MAPTTLINFO result;
    if(IPList.empty()){
		INFO("PingTTL 数量:%zd 为空 源IP:%s",IPList.size(),flag.SrcIP);
		return result;
    }
    InitPingPID();
	flag.PingTimes=20;
	if(flag.PacketSize<80)flag.PacketSize=80;
    ShowPingFlag(flag,IPList.size());
	ReSetStartTime();
	if(flag.bSupportIPV6==1){
		result=_GetTTLInfoByIPList(IPList,flag);
	}else{
		SETIPLIST RealIPList;
		SETIPLIST::iterator it=IPList.begin();
		while(it!=IPList.end()){
			if(IsIPV6(it->c_str())==0){
				RealIPList.insert(it->c_str());
			}
			it++;
		}
		result=_GetTTLInfoByIPList(RealIPList,flag);
	}
    INFO("PingTTL 数量:%zd 结果数量:%zd 源IP:%s 耗时:%d ms",IPList.size(),result.size(),flag.SrcIP,GetEndTime());
    return result;
}
//检查是否有没有响应
int CheckLossNoAnswer(PINGTTLINFOTMP tmpdata,int ttl){
	if(ttl<5||ttl>(DEFAULT_MAX_TIMES-1)){
		return 0;
	}	
	int i=ttl-5;
	while(i<ttl){
		if(tmpdata.fromip[i][0]!='\0'){
			return 0;
		}
		i++;
	}
	return 1;
}
#include"PublicRandom.h"
MAPTTLINFO _GetTTLInfoByIPList(SETIPLIST IPList,PINGFLAG flag){
	PublicRandom IPListTmp;
	IPListTmp.InsertSetList(IPList);
	MAPPINGTTLINFOTMP tmpstatus;
	SETIPPINGCHECK    checklist;
	MAPSTRINGLIST     realiplist;//标准->实际
	int             sendflag=1;//发送标志
	int             checkflag=0;//检测标志
	int             makeflag=0;//构建标志
	int             sendtimes=1;//要从1开始
	struct timeval  sendtimestamp={0,0};
	struct timeval  checktimestamp={0,0};
	struct timeval  maketimestamp={0,0};
	int             sendnum=0;
	int				bStopRunning=0;//紧急停止
	CPublicSocket sock;
	if(sock.CreateSocket(SOCKET_TYPE_ICMP,flag.PerTime,flag.SrcIP)==-1){
		WRONG("Create Socket Error!!!");
		return GetLossStatusByTmp(tmpstatus);
	}	
	do{
		SETIPLIST::iterator it=IPList.begin();
		while(it!=IPList.end()){
			if(IsIPV6(it->c_str())==1){
				realiplist.insert(std::pair<std::string,std::string>(GetSimpleIPV6(it->c_str()),it->c_str()));
			}
			it++;
		}
	}while(0);
	gettimeofday(&sendtimestamp,NULL);
	checktimestamp=sendtimestamp;
	maketimestamp =sendtimestamp;
	while((sendtimes<flag.PingTimes)||(IPListTmp.empty()==false)||(checklist.empty()==false)){
//构建队列		
		if((makeflag==1)&&(IPListTmp.empty()==true)&&(sendtimes<flag.PingTimes)){
			sendtimes++;
			if(IPList.empty()){
				WARN("PINGLOSS探测提前结束");
				sendtimes=flag.PingTimes;
			}else
			if(sendtimes<flag.PingTimes){
				IPListTmp.InsertSetList(IPList);
			}
			makeflag=0;
			INFO("PINGTTL MakeLIST Size:%zd 进度:%d",IPListTmp.size(),sendtimes);
		}
//发送阶段判断		
		while((sendflag==1)&&(IPListTmp.empty()==false)){
			sendflag=0;
			IPPINGCHECK checkinfo;
			memset(&checkinfo,0,sizeof(IPPINGCHECK));
			if(IPListTmp.empty()==false){
				std::string tmp=IPListTmp.PopList();
				strcpy(checkinfo.ip,tmp.c_str());
				checkinfo.seqinfo.flag.id=sendtimes;
				checkinfo.seqinfo.flag.check=flag.CheckID;
				int Ret=SendICMPPacket(&sock,flag.PacketSize,checkinfo,sendtimes);		
				if(Ret==0){
					checklist.insert(checkinfo);
					sendnum++;
					if(sendnum<flag.PerNum){
						sendflag=1;
					}
				}else
				if(Ret==-1){
					//IPListTmp.PushList(checkinfo.ip);
					WRONG("发送失败 IP:%s 在GetTTLInfoByIPList",checkinfo.ip);
				}else
				if(Ret==-2){
					WRONG("发送异常 IP:%s 在GetTTLInfoByIPList",checkinfo.ip);
					//break;
				}else{
					WRONG("发现问题 IP:%s 在GetTTLInfoByIPList",checkinfo.ip);
				}
				if(sendflag==0){
					sendnum=0;
					sendtimestamp=checkinfo.sendtime;
				}
			}else{
				WRONG("不应出现此调用　在GetTTLInfoByIPList");
			}	
		}
//处理抓包	
		int usetime=0;
		do{
			char fromip[40]={0};
			struct timeval timestamp={0};
			IPPINGCHECK checkinfo;
			memset(&checkinfo,0,sizeof(IPPINGCHECK));
			usetime=ReadICMPPacket(&sock,flag.CheckID,checkinfo,timestamp,fromip);		
			if(usetime>-2){
				if(usetime==0){
					MAPSTRINGLIST::iterator itreal=realiplist.find(checkinfo.ip);
					if(itreal!=realiplist.end()){
						strcpy(checkinfo.ip,itreal->second.c_str());
					}
				}

				SETIPPINGCHECK::iterator it=checklist.find(checkinfo);
				if(it!=checklist.end()){
					if(tmpstatus.find(it->ip)==tmpstatus.end()){
						PINGTTLINFOTMP ttlinfo;
						memset(&ttlinfo,0,sizeof(PINGTTLINFOTMP));
						//printf("insert:%s\n",it->ip);
						tmpstatus.insert(std::pair<std::string,PINGTTLINFOTMP>(it->ip,ttlinfo));
						//printf("insert:%send\n",it->ip);
						//ShowTmpStatus(tmpstatus);
					}
					
					MAPPINGTTLINFOTMP::iterator itinfo=tmpstatus.find(it->ip);
					if(it->seqinfo.flag.id<flag.PingTimes){
						itinfo->second.ping[it->seqinfo.flag.id]=usetime;
						strcpy(itinfo->second.fromip[it->seqinfo.flag.id],fromip);
						//通的将从队列中移除
						if(usetime>0){
							IPList.erase(it->ip);
						}
					}else{
						WRONG("不应出现此错误LossApi:%s %d",it->ip,it->seqinfo.flag.id);
					}
					sendflag=1;
					//printf("Find:%s id:%d\n",checkinfo.ip,checkinfo.seqinfo.flag.id);
                }else{
					//printf("Not Find:%s id:%d id:%u\n",checkinfo.ip,checkinfo.seqinfo.flag.id,checkinfo.addr[0]);
				}
            }else
			if(usetime==-3){
				WRONG("接收异常 IP 在GetTTLInfoByIPList");
				sendtimes=flag.PingTimes;
				bStopRunning=1;
				usetime=-11;
			}
		}while(usetime!=-11);	
		struct timeval timestamp;
		memset(&timestamp,0,sizeof(struct timeval));
		gettimeofday(&timestamp,NULL);
		if(GetTimerSubCommon(timestamp,sendtimestamp)>flag.PerTime){
			sendflag=1;
			sendtimestamp=timestamp;
			if(GetTimerSubCommon(timestamp,checktimestamp)>1000){
				checkflag=1;
				checktimestamp=timestamp;
				
			}
			if(GetTimerSubCommon(timestamp,maketimestamp)>flag.PerListTime){
				maketimestamp=timestamp;
				if((IPListTmp.empty()==true)&&(sendtimes<flag.PingTimes)){
					makeflag=1;
				}
			}
		}
		if(checkflag){
			checkflag=0;
			INFO("进行一次PINGTTL超时检测 发送剩余:%zd 发送进度:%d",IPListTmp.size(),sendtimes);
			SETIPPINGCHECK checklistbak=checklist;
			checklist.clear();
			struct timeval timestamp=checktimestamp;
			SETIPPINGCHECK::iterator it=checklistbak.begin();
			while(it!=checklistbak.end()){
				int checktime=GetTimerSubCommon(timestamp,it->sendtime);
//没超时继续等				
				if(checktime<flag.TimeOut){
					checklist.insert(*it);
				}else{
					//printf("TimeOut:%s id:%d id:%u\n",it->ip,it->seqinfo.flag.id,it->addr[0]);
					sendflag=1;
//获取指针			
					MAPPINGTTLINFOTMP::iterator itinfo=tmpstatus.find(it->ip);
					if(itinfo==tmpstatus.end()){
						PINGTTLINFOTMP ttlinfo;
						memset(&ttlinfo,0,sizeof(PINGTTLINFOTMP));
						//printf("insert:%s\n",it->ip);
						tmpstatus.insert(std::pair<std::string,PINGTTLINFOTMP>(it->ip,ttlinfo));
						//printf("insert2:%s\n",it->ip);
						//ShowTmpStatus(tmpstatus);
					}else
//检查空白期，连续5个没响应不再探测
					if(sendtimes>10){
						if(CheckLossNoAnswer(itinfo->second,it->seqinfo.flag.id)==1){
							IPList.erase(it->ip);
						}
					}
				}
				it++;
            }
			if(EnablePingThread()==0||bStopRunning==1){
				INFO("收到停止信号 PINGTTL 退出");
				break;
			}
        }
	}
	return GetLossStatusByTmp(tmpstatus);
}
MAPTTLINFO GetLossStatusByTmp(MAPPINGTTLINFOTMP tmpstatus){
	MAPTTLINFO result;
	MAPPINGTTLINFOTMP::iterator it=tmpstatus.begin();
	while(it!=tmpstatus.end()){
		TTLINFO ttlinfo={0};
		PINGTTLINFOTMP tmpinfo=it->second;
		int i=0;		
		while(i<DEFAULT_MAX_TIMES){
			//printf("i:%d ping:%d ip:%s\n",i,tmpinfo.ping[i],tmpinfo.fromip[i]);
			if(tmpinfo.ping[i]>0){
				ttlinfo.ping=tmpinfo.ping[i];
				ttlinfo.ttl=i;
				break;
			}else
			if(tmpinfo.fromip[i][0]!='\0'){
				ttlinfo.perttl=i;
				strcpy(ttlinfo.fromip,tmpinfo.fromip[i]);
			}	
			i++;
		}
		result.insert(std::pair<std::string,TTLINFO>(it->first.c_str(),ttlinfo));		
		it++;
	}
	return result;
}
void ShowTTLStatusInfo(MAPTTLINFO IPList,bool bSave,bool bShow){
	MAPTTLINFO::iterator it=IPList.begin();
	while(it!=IPList.end()){
		INFO("IP:%s PING:%d TTL:%d LastTTL:%d LastIP:%s",it->first.c_str(),
			it->second.ping,it->second.ttl,
			it->second.perttl,it->second.fromip);
		it++;
	}
}
#endif